package com.ly.wxstore.service.weixin;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ly.wxstore.comm.DateUtil;
import com.ly.wxstore.comm.DecimalFormatUtils;
import com.ly.wxstore.comm.Error;
import com.ly.wxstore.comm.MyRandomUtils;
import com.ly.wxstore.comm.WeixinPaySignUtils;
import com.ly.wxstore.comm.WxPayReturnCode;
import com.ly.wxstore.comm.XmlUtils;
import com.ly.wxstore.entity.goods.GoodsOrder;
import com.ly.wxstore.entity.goods.GoodsOrderDetail;
import com.ly.wxstore.entity.weixin.WeixinPublic;
import com.ly.wxstore.exception.ServerException;
import com.ly.wxstore.utils.HttpUtil;
import com.ly.wxstore.web.OrderController;

/**
 * 微信支付Service
 * 
 * @author Peter
 *
 */
@Component
public class WeixinPayService {

	private static Logger logger = LoggerFactory.getLogger(OrderController.class);

	@Autowired
	private WeixinPublicService weixinPublicService;

	/**
	 * 微信公众号支付 1.调用统一下单接口 2.返回微信H5 API所需参数
	 * 
	 * @return
	 */
	public Map<String, String> publicPay(GoodsOrder goodsOrder, String openid) {
		// 对接微信支付：调用微信统一下单接口
		WeixinPublic weixinPublic = weixinPublicService.getGlobleWeixinPublic();
		String prepay_id = invokWeixinPayUnifiedorder(goodsOrder, weixinPublic, openid);

		// 生成页面H5接口所需参数
		Map<String, String> params = genWeixinPayParams(weixinPublic, prepay_id);

		return params;
	}

	private Map<String, String> genWeixinPayParams(WeixinPublic weixinPublic, String prepay_id) {
		Map<String, String> params = new HashMap<String, String>();
		params.put("appId", weixinPublic.getAppId());
		params.put("timeStamp", DateUtil.getTimeStamp());
		params.put("nonceStr", MyRandomUtils.getRandom32String());
		params.put("package", "prepay_id=" + prepay_id);//微信生成的预支付回话标识，用于后续接口调用中使用，该值有效期为2小时
		params.put("signType", "MD5");

		// H5API最后参与签名的参数有appId, timeStamp, nonceStr, package,
		// signType，因此payApiKey为null
		String paySign = WeixinPaySignUtils.payUniSign(params, weixinPublic.getPayApiKey());

		params.put("paySign", paySign);
		return params;
	}

	/**
	 * 对接微信支付：调用微信统一下单接口
	 * 
	 * @param goodsOrder
	 */
	private String invokWeixinPayUnifiedorder(GoodsOrder goodsOrder, WeixinPublic weixinPublic, String openid) {
		// 拼装统一下单参数
		Map<String, String> params = new HashMap<String, String>();

		params.put("appid", weixinPublic.getAppId());// 公众账号ID：微信分配的公众账号ID（企业号corpid即为此appId）
		params.put("mch_id", weixinPublic.getMchId());// 商户号：微信支付分配的商户号
		params.put("device_info", "WEB");// 设备号：终端设备号(门店号或收银设备ID)，注意：PC网页或公众号内支付请传"WEB"
		params.put("nonce_str", MyRandomUtils.getRandom32StringUpperCase());// 随机字符串：随机字符串，不长于32位。
		params.put("body", goodsOrder.getGoodsList().get(0).getGoodsName());// 商品描述：商品或支付单简要描述
		params.put("detail", getGoodsDetals(goodsOrder));// 商品详情：商品名称明细列表
		params.put("out_trade_no", goodsOrder.getOrderCode());// 商户订单号：商户系统内部的订单号,32个字符内、可包含字母
		params.put("fee_type", "CNY");// 货币类型：符合ISO 4217标准的三位字母代码，默认人民币：CNY
		params.put("total_fee", DecimalFormatUtils.formatFen(goodsOrder.getRealMoney()));// 货币类型：符合ISO
																							// 4217标准的三位字母代码，默认人民币：CNY
		params.put("spbill_create_ip", goodsOrder.getClientIp());// 终端IP：APP和网页支付提交用户端ip，Native支付填调用微信支付API的机器IP
		params.put("time_start", DateUtil.sdf_2.format(goodsOrder.getCreateDate()));// 交易起始时间：订单生成时间，格式为yyyyMMddHHmmss，如2009年12月25日9点10分10秒表示为20091225091010
		params.put("time_expire", DateUtil.sdf_2.format(goodsOrder.getExpireDate()));// 交易结束时间：订单失效时间，格式为yyyyMMddHHmmss，如2009年12月27日9点10分10秒表示为20091227091010。其他详见时间规则。注意：最短失效时间间隔必须大于5分钟。
		// params.put("goods_tag",
		// DateUtil.sdf_2.format(goodsOrder.getExpireDate()));//商品标记：商品标记，代金券或立减优惠功能的参数，说明详见代金券或立减优惠
		params.put("notify_url", weixinPublic.getPayNotifyUrl());// 通知地址：接收微信支付异步通知回调地址
		params.put("trade_type", "JSAPI");// 交易类型：取值如下：JSAPI，NATIVE，APP，WAP
		// params.put("product_id",
		// "");//商品ID：trade_type=NATIVE，此参数必传。此id为二维码中包含的商品ID，商户自行定义。
		// params.put("limit_pay", "");//指定支付方式String(32)：no_credit--指定不能使用信用卡支付
		params.put("openid", openid);// 用户标识String(128)：trade_type=JSAPI，此参数必传，用户在商户appid下的唯一标识。下单前需要调用【网页授权获取用户信息】接口获取到用户的Openid。企业号请使用【企业号OAuth2.0接口】获取企业号内成员userid，再调用【企业号userid转openid接口】进行转换

		// 参数进行签名
		String reqXml = WeixinPaySignUtils.getXmlStr(params, weixinPublic.getPayApiKey());
		String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";

		String respXml = "";
		try {
			respXml = HttpUtil.httpPost(url, reqXml);
		} catch (Exception e) {
			logger.error(e.getMessage(), e);
			throw new ServerException(Error._40002, Error._40002_MSG);
		}

		Map<String, String> resultMap = null;
		try {
			resultMap = XmlUtils.toMap(respXml);
		} catch (DocumentException e) {
			logger.error(e.getMessage(), e);
			throw new ServerException(Error._40003, Error._40003_MSG);
		}

		// 返回参数校验
		String return_code = resultMap.get("return_code");
		String return_msg = resultMap.get("return_msg");
		if (!WxPayReturnCode.SUCCESS.equals(return_code)) {
			logger.error("return_code:" + return_code + ",return_msg:" + return_msg);
			throw new ServerException(Error._40004, Error._40004_MSG);
		}

		String result_code = resultMap.get("result_code");
		String err_code = resultMap.get("err_code");
		String err_code_des = resultMap.get("err_code_des");
		String sign = resultMap.get("sign");

		// 网络数据正确性校验
		String mySign = WeixinPaySignUtils.payUniSign(resultMap, weixinPublic.getPayApiKey());
		if (!mySign.equals(sign)) {
			logger.error("调用微信统一下单接口返回数据被篡改：mySign:" + mySign + ",sign:" + sign);

			throw new ServerException(Error._40005, Error._40005_MSG);
		}

		if (!WxPayReturnCode.SUCCESS.equals(result_code)) {
			logger.error("result_code:" + return_code + ",err_code:" + err_code + ",err_code_des:" + err_code_des);
			throw new ServerException(Error._40004, Error._40004_MSG);
		}

		return resultMap.get("prepay_id");

	}

	/**
	 * 商品详情：商品名称明细列表
	 * 
	 * @param goodsOrder
	 * @return
	 */
	private String getGoodsDetals(GoodsOrder goodsOrder) {
		StringBuffer details = new StringBuffer();
		List<GoodsOrderDetail> list = goodsOrder.getGoodsList();
		for (GoodsOrderDetail goodsOrderDetail : list) {
			details.append(goodsOrderDetail.getGoodsName());
		}
		return details.toString();
	}

}
